#include "devices.h"
#include "conn_tab.h"

#define MAX_SIZE_DIGITS 10	//max chars to copy to user buffer

static int major;						
struct device *dv = NULL;            	// Global variable for the character device	
static struct class *cl = NULL;      	// Global variable for the device class
static struct cdev rulesCdev;       	// Global variable for the character device structure
static dev_t rulesDev;
static struct cdev logCdev;       		// Global variable for the character device structure
static dev_t logDev;
static struct cdev connCdev;       		// Global variable for the character device structure
static dev_t connDev;

// rules device operations
static struct file_operations rules_fops = {
	open: rules_open,	
	mmap: rules_mmap,
	owner: THIS_MODULE,	
	
};

// config file operations 
struct device_attribute config_attrs = {
		.attr.name = "config",		
		.store = config_store,
		.show = config_show,
		.attr.mode = FW_ATTR_MODE,
};

// rules_size file operations 
struct device_attribute rules_size_attrs = {
		.attr.name = "rules_size",		
		.store = NULL,
		.show = rules_size_show,
		.attr.mode = FW_ATTR_MODE,
};

// log device operations
static struct file_operations log_fops = {
	open: log_open,	
	mmap: log_mmap,
	owner: THIS_MODULE,	
};

// log_clear file operations 
struct device_attribute log_clear_attrs = {
		.attr.name = "log_clear",		
		.store = log_clear_store,
		.show = NULL,
		.attr.mode = FW_ATTR_MODE,
};

// log_size file operations 
struct device_attribute log_size_attrs = {
		.attr.name = "log_size",		
		.store = NULL,
		.show = log_size_show,
		.attr.mode = FW_ATTR_MODE,
};

// conn table device operations
static struct file_operations conn_fops = {
	open: conn_open,	
	mmap: conn_mmap,
	owner: THIS_MODULE,	
};

// conn_tab_clear file operations 
struct device_attribute conn_clear_attrs = {
		.attr.name = "conn_tab_clear",		
		.store = conn_clear_store,
		.show = NULL,
		.attr.mode = FW_ATTR_MODE,
};

// conn_tab_size file operations 
struct device_attribute conn_size_attrs = {
		.attr.name = "conn_tab_size",		
		.store = NULL,
		.show = conn_size_show,
		.attr.mode = FW_ATTR_MODE,
};


static ssize_t config_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count){
	unsigned long converted;
	char *p = (char *)buf;	
	converted = simple_strtoul(p,&p,10);
	if (!p || (*p && (*p != '\n'))){
		return -EINVAL;		
	}
	if(converted > UINT_MAX){		
		return -ERANGE;
	}	
	fw_config = converted;
	printk("\nconfig was updated\n");	
	return count;	
}

static ssize_t config_show(struct device *dev, struct device_attribute *attr,char *buf){
	char configStr[MAX_SIZE_DIGITS] = { 0 };	
	snprintf(configStr,MAX_SIZE_DIGITS,"%d",fw_config);
	configStr[MAX_SIZE_DIGITS-1] = '\0';
	memcpy(buf, configStr, sizeof(configStr));
	return strlen(configStr);
}

static ssize_t rules_size_show(struct device *dev, struct device_attribute *attr,char *buf){
	char size[MAX_SIZE_DIGITS] = { 0 };		
	snprintf(size,MAX_SIZE_DIGITS,"%d",RULE_BASE_ENTRIES*sizeof(rule_t));
	size[MAX_SIZE_DIGITS-1] = '\0';
	memcpy(buf, size, sizeof(size));       
    return strlen(size);
}

static int log_open(struct inode *inode,struct file *filep){
	return 0;
}

static int log_mmap(struct file *filp, struct vm_area_struct *vma){
	long unsigned int size = vma->vm_end - vma->vm_start;   
	if (remap_pfn_range(vma, vma->vm_start,__pa(log.rows) >> PAGE_SHIFT,size,vma->vm_page_prot)< 0){
		printk("fw4 mmap failed to map\n");				  
		return -EAGAIN;
	}
    printk("fw4 mmap done\n");  
    return 0;	

}

static ssize_t log_size_show(struct device *dev, struct device_attribute *attr,char *buf){	
	char size[MAX_SIZE_DIGITS] = { 0 };
	snprintf(size,MAX_SIZE_DIGITS,"%d",LOG_ENTRIES*sizeof(log_row_t));
	size[MAX_SIZE_DIGITS-1] = '\0';
	memcpy(buf,size,sizeof(size));
	return strlen(size);
}

static ssize_t log_clear_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count){
	unsigned int bufferLen = strnlen(buf,PAGE_SIZE);
	if(bufferLen > 0){
		clearLog();
	}
	return bufferLen;
}

static int conn_open(struct inode *inode,struct file *filep){
	return 0;
}

static int conn_mmap(struct file *filp, struct vm_area_struct *vma){
	long unsigned int size = vma->vm_end - vma->vm_start;   
	if (remap_pfn_range(vma, vma->vm_start,__pa(connTablePtr) >> PAGE_SHIFT,size,vma->vm_page_prot)< 0){
		printk("fw4 mmap failed to map\n");				  
		return -EAGAIN;
	}
    printk("fw4 mmap done\n");  
    return 0;	

}

static ssize_t conn_size_show(struct device *dev, struct device_attribute *attr,char *buf){
	char size[MAX_SIZE_DIGITS] = { 0 };
	snprintf(size,MAX_SIZE_DIGITS,"%d",CONNECTION_TABLE_ENTRIES*sizeof(connection_t));
	size[MAX_SIZE_DIGITS-1] = '\0';
	memcpy(buf,size,sizeof(size));
	return strlen(size);
}

static ssize_t conn_clear_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count){
	unsigned int bufferLen = strnlen(buf,PAGE_SIZE);
	if(bufferLen > 0){
		clearConnectionTable();
	}
	return bufferLen;
}

static int rules_open(struct inode *inode,struct file *filep){
	return 0;
}

// function for mmap request for /dev/rules
static int rules_mmap(struct file *filp, struct vm_area_struct *vma){  
  long unsigned int size = vma->vm_end - vma->vm_start;   
  if (remap_pfn_range(vma, vma->vm_start,__pa(rulesPtr) >> PAGE_SHIFT,size,vma->vm_page_prot)< 0){
		printk("fw4 mmap failed to map\n");				  
		return -EAGAIN;
	}
    printk("fw4 mmap done\n");  
    return 0;	
}
